package logger

import (
	"os"
	"strings"
	"time"

	"github.com/sirupsen/logrus"
)

// Level represents the logging level
type Level string

const (
	LevelTrace Level = "trace"
	LevelDebug Level = "debug"
	LevelInfo  Level = "info"
	LevelWarn  Level = "warn"
	LevelError Level = "error"
	LevelFatal Level = "fatal"
	LevelPanic Level = "panic"
)

// Format represents the log output format
type Format string

const (
	FormatJSON Format = "json"
	FormatText Format = "text"
)

// Config holds the logger configuration
type Config struct {
	Level  Level  `yaml:"level"`  // Log level (trace, debug, info, warn, error, fatal, panic)
	Format Format `yaml:"format"` // Output format (json, text)
	Output string `yaml:"output"` // Output destination (stdout, stderr, file path)
}

// DefaultConfig returns the default logger configuration
func DefaultConfig() *Config {
	return &Config{
		Level:  LevelInfo,
		Format: FormatJSON,
		Output: "stdout",
	}
}

// Logger wraps logrus.Logger with additional functionality
type Logger struct {
	*logrus.Logger
	config *Config
}

// New creates a new logger instance with the given configuration
func New(config *Config) *Logger {
	if config == nil {
		config = DefaultConfig()
	}

	logger := logrus.New()

	// Set log level
	level, err := logrus.ParseLevel(string(config.Level))
	if err != nil {
		level = logrus.InfoLevel
	}
	logger.SetLevel(level)

	// Set formatter
	switch config.Format {
	case FormatJSON:
		logger.SetFormatter(&logrus.JSONFormatter{
			TimestampFormat: time.RFC3339Nano,
			FieldMap: logrus.FieldMap{
				logrus.FieldKeyTime:  "timestamp",
				logrus.FieldKeyLevel: "level",
				logrus.FieldKeyMsg:   "message",
				logrus.FieldKeyFunc:  "caller",
			},
		})
	case FormatText:
		logger.SetFormatter(&logrus.TextFormatter{
			TimestampFormat: time.RFC3339,
			FullTimestamp:   true,
		})
	default:
		logger.SetFormatter(&logrus.JSONFormatter{
			TimestampFormat: time.RFC3339Nano,
		})
	}

	// Set output
	switch strings.ToLower(config.Output) {
	case "stdout":
		logger.SetOutput(os.Stdout)
	case "stderr":
		logger.SetOutput(os.Stderr)
	default:
		// Treat as file path
		file, err := os.OpenFile(config.Output, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
		if err != nil {
			logger.WithError(err).Warn("Failed to open log file, falling back to stdout")
			logger.SetOutput(os.Stdout)
		} else {
			logger.SetOutput(file)
		}
	}

	return &Logger{
		Logger: logger,
		config: config,
	}
}

// WithComponent creates a new logger with a component field
func (l *Logger) WithComponent(component string) *logrus.Entry {
	return l.WithField("component", component)
}

// WithNodeID creates a new logger with a node_id field
func (l *Logger) WithNodeID(nodeID string) *logrus.Entry {
	return l.WithField("node_id", nodeID)
}

// WithVIP creates a new logger with VIP-related fields
func (l *Logger) WithVIP(vip string, interfaceName string) *logrus.Entry {
	return l.WithFields(logrus.Fields{
		"vip":       vip,
		"interface": interfaceName,
	})
}

// WithPeer creates a new logger with peer-related fields
func (l *Logger) WithPeer(peerID string, peerIP string) *logrus.Entry {
	return l.WithFields(logrus.Fields{
		"peer_id": peerID,
		"peer_ip": peerIP,
	})
}

// WithHealthCheck creates a new logger with health check fields
func (l *Logger) WithHealthCheck(name string, ok bool, detail string) *logrus.Entry {
	return l.WithFields(logrus.Fields{
		"check_name":   name,
		"check_ok":     ok,
		"check_detail": detail,
	})
}

// WithElection creates a new logger with election-related fields
func (l *Logger) WithElection(leaderID string, priority int32, quorumSize int) *logrus.Entry {
	return l.WithFields(logrus.Fields{
		"leader_id":   leaderID,
		"priority":    priority,
		"quorum_size": quorumSize,
	})
}

// WithNetwork creates a new logger with network-related fields
func (l *Logger) WithNetwork(protocol string, address string) *logrus.Entry {
	return l.WithFields(logrus.Fields{
		"protocol": protocol,
		"address":  address,
	})
}

// GetConfig returns the logger configuration
func (l *Logger) GetConfig() *Config {
	return l.config
}

// SetLevel dynamically changes the log level
func (l *Logger) SetLevel(level Level) {
	logLevel, err := logrus.ParseLevel(string(level))
	if err != nil {
		l.Warnf("Invalid log level %s, keeping current level", level)
		return
	}
	l.Logger.SetLevel(logLevel)
	l.config.Level = level
}

// Global logger instance
var global *Logger

// Init initializes the global logger with the given configuration
func Init(config *Config) {
	global = New(config)
}

// Get returns the global logger instance
func Get() *Logger {
	if global == nil {
		global = New(DefaultConfig())
	}
	return global
}

// Convenience functions for global logger
func WithComponent(component string) *logrus.Entry {
	return Get().WithComponent(component)
}

func WithNodeID(nodeID string) *logrus.Entry {
	return Get().WithNodeID(nodeID)
}

func WithVIP(vip string, interfaceName string) *logrus.Entry {
	return Get().WithVIP(vip, interfaceName)
}

func WithPeer(peerID string, peerIP string) *logrus.Entry {
	return Get().WithPeer(peerID, peerIP)
}

func WithHealthCheck(name string, ok bool, detail string) *logrus.Entry {
	return Get().WithHealthCheck(name, ok, detail)
}

func WithElection(leaderID string, priority int32, quorumSize int) *logrus.Entry {
	return Get().WithElection(leaderID, priority, quorumSize)
}

func WithNetwork(protocol string, address string) *logrus.Entry {
	return Get().WithNetwork(protocol, address)
}

// Standard logrus functions for global logger
func Trace(args ...interface{}) {
	Get().Trace(args...)
}

func Debug(args ...interface{}) {
	Get().Debug(args...)
}

func Info(args ...interface{}) {
	Get().Info(args...)
}

func Warn(args ...interface{}) {
	Get().Warn(args...)
}

func Error(args ...interface{}) {
	Get().Error(args...)
}

func Fatal(args ...interface{}) {
	Get().Fatal(args...)
}

func Panic(args ...interface{}) {
	Get().Panic(args...)
}

func Tracef(format string, args ...interface{}) {
	Get().Tracef(format, args...)
}

func Debugf(format string, args ...interface{}) {
	Get().Debugf(format, args...)
}

func Infof(format string, args ...interface{}) {
	Get().Infof(format, args...)
}

func Warnf(format string, args ...interface{}) {
	Get().Warnf(format, args...)
}

func Errorf(format string, args ...interface{}) {
	Get().Errorf(format, args...)
}

func Fatalf(format string, args ...interface{}) {
	Get().Fatalf(format, args...)
}

func Panicf(format string, args ...interface{}) {
	Get().Panicf(format, args...)
}

func WithField(key string, value interface{}) *logrus.Entry {
	return Get().WithField(key, value)
}

func WithFields(fields logrus.Fields) *logrus.Entry {
	return Get().WithFields(fields)
}

func WithError(err error) *logrus.Entry {
	return Get().WithError(err)
}
